local oo = require("SharedLibs.BaseLib")
Debugger = require("SharedLibs.Debugger");
AOS = require("SharedLibs.AsyncOpState")
AsyncError = require("SharedLibs.AsyncError");
AsyncOp = require("SharedLibs.AsyncOp");
SyncOp = require("SharedLibs.Ops.SyncOp");
LambdaOp = require("SharedLibs.Ops.LambdaOp");

Seq = require("SharedLibs.FP.Sequence");

_IsStudio= Debugger.IsStudio;

local co = {}

local coroHooker = require("SharedLibs.CoroHooker");
co.yield = coroHooker.Yield;
coroHooker.Init(co);

co.LambdaOp = LambdaOp;

--- Stop里要用LambdaOp, 所以只能把这两个函数放这里
function co.Lambda(lambda)
	local op = oo.New(co.LambdaOp, lambda);
	return co.yield(op);
end

function co.NewLambda(lambda)
	return oo.New(co.LambdaOp, lambda);
end

co.AsyncOpState = AOS;

--- 统计嵌套的Coro的数量，可以用来判断当前代码是否在Coro里（见CoroutineHooker.yield）
--- _NestedCount > 0表示一定是在一个coro的内部代码里
_NestedCount = 0;
__AliveCos = {}
__AliveCount = 0;

function co.GetCurrent()
	return __AliveCos[coroutine.running()];
end

--- Coroutine Class ---
co.Coroutine = oo.Class()
local Coroutine = co.Coroutine
Coroutine.name = "Coroutine"
oo.ClassExtends(Coroutine, AsyncOp)

function co.New(coFunc, ...)
	local co = oo.New(co.Coroutine, coFunc, ...);
	return co;
end

--- Run a standalone coroutine
function co.Run(coFunc, ...)
	local co = oo.New(co.Coroutine, coFunc, ...);
	co:Start()
	return co;
end

--- Yield a coroutine (in a co) and return it's result
function co.Wait(coFunc, ...)
	local co = oo.New(co.Coroutine, coFunc, ...);
	return co.yield(co);
end

--- 在RawCo里调用 CoRun 可以等待一个Co结束，并返回它的返回值
function co.CoRun(coFunc, ...)
	if co.GetCurrent() then
		local msg = "CoRun cannot be called in a co!(can be called in raw lua thread only!)";
		print(msg); error(msg);
	end

	local co_ = oo.New(co.Coroutine, coFunc, ...);

	local rawCo = coroutine.running();
	local onComplete = function(val)
		coroutine.resume(rawCo, val);	
	end
	local onError = function(err)
		warn(tostring(err));
		--- 发生错误了，返回nil
		coroutine.resume(rawCo, co_:GetReturn());	
	end	
	co_:OnComplete(onComplete);
	co_:OnError(onError);

	co_:Start()
	return coroutine.yield();
end

--- 返回一个函数，这个函数每次调用，都会用一个新的coro来执行coFunc
function co.Colize(coFunc, ...)
	local args = {...};
	return function(...) 
		local co = oo.New(co.Coroutine, coFunc, table.unpack(args), ...);
		co:Start()
	end	
end

--- colass是将class和coroutine融合到一起的类
function co.Colass(clazz)		
	function clazz:newCo(func, ...)
		return co.New(func, self, ...);
	end
	function clazz:runCo(func, ...)
		return co.Run(func, self, ...);
	end
	function clazz:colize(func, ...)
		return co.Colize(func, self, ...);
	end

end

function Coroutine.prototype:SetCoroFunc(func, ...)
	assert(self._state == AOS.Ready or self._state == AOS.Stopped);
	self._func = func;
	self._iterator = coroutine.create(func)
	self._args = {...};	
	
	if _IsStudio then
		local info = debug.getinfo(func, "S");
		local fileName = info.short_src;
		local line = info.linedefined;
		--- 找到最后一个.号后的文件名. 这个正则由ChatGPT生成, 可以直接问GPT其含义
		fileName = string.match(fileName, "%.([^%.]+)$");
		self._name = "Coro - "..fileName..": "..line;
	end
end

--- 覆盖默认的Restart方法，增加传递参数支持
function Coroutine.prototype:Restart(...)
	self:Reset(...); 
	---self._args = {...};
	self:Start();
end

function Coroutine.prototype:SetParams(...)
	self._args = {...};	
end

function Coroutine.prototype:SetParams2(args)
	self._args = args;
end

function Coroutine.prototype:_onReset(...)
	self._args = {...};
	self._iterator = coroutine.create(self._func);
end

function Coroutine.prototype.constructor(self, func, ...)
	Coroutine._ss.prototype.constructor(self)

	---
	self._onCoStart = nil	
	self._onStopped = nil
	self._onComplete = nil
	self._curOp = nil
	self._returnValue = nil
	
	self._continuation = nil

	self:SetCoroFunc(func, ...);

	self._handleNext = function(self, op)
		local needContinue = true;

		local curIter = self._iterator;

		while needContinue do
			if self._state == AOS.Stopped then break end;

			needContinue = false;
			local isSuccess, iteratorRet = nil, nil;

			if self._curOp ~= nil then
				--- 注意：_NestedCount-1之前不能有return！！！
				--- 另外，_NestedCount+1和_NestedCount-1之间不能调用任何方法
				--- 外部方法的异常会导致_NestedCount计数错误。这个技术错误会导致后续
				--- 整个Coro系统都无法正常运转！！！
				_NestedCount = _NestedCount + 1;			
				isSuccess, iteratorRet = coroutine.resume(
					curIter,
					self._curOp:GetReturn()
				)
				_NestedCount = _NestedCount - 1;
			else
				--- 注意：_NestedCount-1之前不能有return！！！		
				_NestedCount = _NestedCount + 1;
				--- 刚开始执行时
				isSuccess, iteratorRet = 
					coroutine.resume(
						curIter,
						table.unpack(self._args)
					)
				_NestedCount = _NestedCount - 1;			
			end	

			--- 在Resume后的代码里如果调用了当前Coro的Stop时，或者Stop后如果再Reset(), Restart(), 
			--- 那么self._iterator ~= curIter
			local isOldIter = self._iterator == curIter;
			if isOldIter then
				local status = coroutine.status(curIter);
				if status == "dead" then			
					if not isSuccess then
						local errMsg = tostring(iteratorRet).."\n"..debug.traceback(self._iterator); 
						self:HandleCodeException(oo.New(AsyncError, errMsg, self, true))
					else
						self:_doComplete(iteratorRet);
					end
				else
					if isSuccess then
						if iteratorRet == nil then
							self._curOp = SyncOp:Default(nil)
						else
							if type(iteratorRet) ~= "table" then
								self._curOp = SyncOp:Default(iteratorRet)
							else
								if iteratorRet.classID == "IAsyncOp" then
									self._curOp = iteratorRet;
								else
									self._curOp = SyncOp:Default(iteratorRet);
								end
							end
						end

						if self._curOp then
							local oldOp = self._curOp;
							self._curOp:Start(self._handleNext, self, true)

							--- 在_curOp的Start的代码里如果调用了当前Coro的Stop时，或者Stop后如果再Reset(), Restart(), 
							--- 那么self._iterator ~= curIter
							if self._iterator == curIter then
								if self._curOp then
									if self._state == AOS.Paused then
										self._curOp:Pause();
									end									
									needContinue = self._curOp:IsDone();
								end
							end
						end
					else
						local errMsg = tostring(iteratorRet).."\n"..debug.traceback(self._iterator);
						self:HandleCodeException(oo.New(AsyncError, errMsg, self, true))
					end
				end
			end
		end
	end
end

oo.SetDescriptor(
	Coroutine.prototype,
	"isCombinedOp",
	{
		get = function(self)
			return true;
		end
	},
	true
)
oo.SetDescriptor(
	Coroutine.prototype,
	"iterator",
	{
		get = function(self)
			return self._iterator
		end
	},
	true
)
oo.SetDescriptor(
	Coroutine.prototype,
	"state",
	{
		get = function(self)
			return self._state
		end
	},
	true
)

--- 只要Co启动过, 之后一定会触发onStop
function Coroutine.prototype:OnStop(onStop)
	assert(self._state ~= AOS.Stopped);
	self._onStopped = onStop
	return self
end

--- Co正常结束才会触发onComplete
-- type OnComplete = (any) -> any
function Coroutine.prototype.OnComplete(self, onComplete)
	assert(not self.isAlive)
	self._onComplete = onComplete
	return self
end

function Coroutine.prototype._OnStart(self, onStart)
	self._onCoStart = onStart
	return self
end

function Coroutine.prototype.GetChildren(self)
	return Seq.fromYield(function()
		if self._curOp == nil then
			return;
		else
			coroutine.yield(self._curOp);
		end
	end);
end

Coroutine.prototype.GetCurOps = Coroutine.prototype.GetChildren;

function Coroutine.prototype:SetName(name)
	self._name = name
	return self
end

function Coroutine.prototype:GetReturn()
	return self._returnValue
end

--[[
如果ignoreCode为true, 那么如果是在当前co里调用的Stop, 就会忽略Stop后面的代码(不会执行).
例如:
	local co1 = g.run(function() 
		local a = 0;
		g.delay(1);
		Stop();
		co1:Stop(true);  --- Executed!不会被打印!
		warn("Executed!");
	end)
]]
function Coroutine.prototype:Stop(ignoreCode)
	if self._state == AOS.Stopped then return end
	
	if ignoreCode then
		local coro = co.GetCurrent();
		if coro == self then
			--- 通过LambdaOp来终止co, 在终止后, Stop()后面的代码自然都不会执行. 
			co.Lambda(function()
				self:_doStop(false);
			end)			
		else
			self:_doStop(false);
		end
	else
		self:_doStop(false);
	end
end

function Coroutine.prototype.Pause(self)
	if (AOS.Ready == self._state) or (AOS.Stopped == self._state) then
		return
	end
	self._state = AOS.Paused
	if self._curOp ~= nil then self._curOp:Pause() end;
end

function Coroutine.prototype.Resume(self)
	if AOS.Ready == self._state or AOS.Stopped == self._state then
		return
	end
	self._state = AOS.Executing
	if self._curOp ~= nil then self._curOp:Resume() end;	
end

function Coroutine.prototype:Start(continuation, parent)
	if (self._state == AOS.Executing) or (self._state == AOS.Paused) then
		self:Stop()
	end
	self._continuation = continuation
	self._returnValue = nil;

	if self._onCoStart ~= nil then
		self:_onCoStart(self)
	end

	__AliveCos[self._iterator] = self;
	__AliveCount = __AliveCount + 1;
	if __AliveCount > 1000 then
		if _IsStudio then
			warn("Too many coro, count: "..__AliveCount);
		end
	end

	self._state = AOS.Executing
	self._parent = parent
	self:_handleNext(nil)
end

function Coroutine.prototype._doComplete(self, returnValue)
	if self._state == AOS.Stopped then return end

	self._returnValue = returnValue
	if self._onComplete ~= nil then
		local preIterator = self._iterator;
		self._onComplete(returnValue)

		--- 还是当前的iterator, 才能继续执行当前的_doStop()
		if preIterator == self._iterator then
			self:_doStop(true);
		end
	else
		self:_doStop(true)
	end
end

function Coroutine.prototype:_GetRawCo()
	return self._iterator;
end

function Coroutine.prototype._doStop(self, isComplete)
	if not self.isAlive then
		return
	end
	
	assert(self._iterator);
	__AliveCos[self._iterator] = nil;
	__AliveCount = __AliveCount - 1;
	--print("Stop Co ####");
	
	self._state = AOS.Stopped
	if self._curOp ~= nil then
		self._curOp:Stop()
		self._curOp = nil
	end
	if self._onStopped ~= nil then
		self:_onStopped(self)
	end
	self._iterator = nil

	local c = self._continuation;
	if c ~= nil then
		self._continuation = nil;
		if isComplete then
			c(self._parent, self)
		end
	end
end

return co;
